package com.zeedev.flyingsaucer.modules.BeerDatabaseModule.impl.members;

import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.net.Uri.Builder;
import android.provider.BaseColumns;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
import com.zeedev.flyingsaucer.data.BeerUri;
import com.zeedev.flyingsaucer.global.UfoClubApp;
import com.zeedev.flyingsaucer.modules.BeerDatabaseModule.interfaces.ListModel;
import mobi.zeedev.flyingsaucer.common.CredentialsModule.interfaces.IPreferenceCredentials;
import mobi.zeedev.flyingsaucer.common.DataModule.FlyingSaucerConv.Beers;
import mobi.zeedev.flyingsaucer.common.DataModule.FlyingSaucerConv.Members;
import mobi.zeedev.flyingsaucer.common.DataModule.FlyingSaucerConv.Members.MemberBeer;
import mobi.zeedev.flyingsaucer.common.DataModule.MetaBeer;
import mobi.zeedev.flyingsaucer.common.DataModule.Util;
import mobi.zeedev.flyingsaucer.common.RegexModule.impl.ParseException;
import mobi.zeedev.flyingsaucer.common.RegexModule.interfaces.IRegexQueries;
import mobi.zeedev.flyingsaucer.common.WebLinkModule.impl.UfoClubClientImpl;
import mobi.zeedev.flyingsaucer.common.WebLinkModule.interfaces.UfoClubClient;
import mobi.zeedev.flyingsaucer.common.WebQueryModule.impl.HttpException;
import roboguice.util.Ln;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Map.Entry;

public class MemberBeerListModel implements Serializable, ListModel {

    @Inject
    @Named("autoIPreferenceCredentials")
    Provider<IPreferenceCredentials> credentials;

    @Inject
    Context mContext;

    @Inject
    ContentResolver mContentResolver;

    @Inject
    @Named("memberId")
    Provider<Integer> memberIdProvider;

    @Inject
    Provider<BeerUri> beerUriProvider;

    BeerUri beerUri;

//    @Inject
//    @Named("isTasted")
//    Boolean isTasted;

    @Inject
    @Named("memberDbId")
    Provider<Long> memberDbIdProvider;

    @Inject
    @Named("isDatabaseEmpty")
    Provider<Boolean> isEmpty;

    @Inject
    UfoClubClient ufoClubClient;

    @Inject
    IRegexQueries regexQueries;

    protected Boolean shouldCancel = false;

    @Inject
    public MemberBeerListModel() {
        super();
    }

    @Override
    public void destroy() {
        // do nothing
        //TODO ???
        //((UfoClubApp)((Activity) mContext).getApplication()).setMemberBeersDownloader(null);

    }

    private void insertAllMember(List<ContentValues> userBeerListIn, Date now, String identKey) {
        //TODO completely make this generic
        this.insertAll(userBeerListIn, now, MemberBeer.CONTENT_URI, MemberBeer.MEMBER_DB_ID, identKey);
    }

    private void insertAll(List<ContentValues> userBeerListIn, Date now, Uri mergeTable, String identColumn, String identKey) {
        List<ContentValues> bulkInsertBeers = new ArrayList<ContentValues>(userBeerListIn.size());
        List<ContentValues> bulkInsertStoreBeer = new ArrayList<ContentValues>(userBeerListIn.size());

        long timeNow = now.getTime();

        for (int i = 0; i < userBeerListIn.size(); i++) {

            ContentValues cvBeers = userBeerListIn.get(i);
            ContentValues cvMergeBeer = new ContentValues();

//				String currentToInsertBeerId = cvBeers.getAsString(Beers.BEER_ID);

//				if (now != null) {
//					//cv.put(MetaBeer.INTERNAL_DATE_ADDED, now.getTime());
//					cvMergeBeer.put(Members.MemberBeer.UPDATED_DATE, now.getTime());
//				}
            cvMergeBeer.put(MemberBeer.ACTIVE, true);
            cvMergeBeer.put(identColumn, identKey);
//				cvMergeBeer.put(Beers.BEER_ID, currentToInsertBeerId );
            cvMergeBeer.put(MemberBeer.CREATION_DATE, timeNow);
            cvMergeBeer.put(MemberBeer.UPDATED_DATE, timeNow);


            String comments = cvBeers.getAsString(MetaBeer.COMMENTS);
            String rating = cvBeers.getAsString(MetaBeer.RATING);
            Long tasted = cvBeers.getAsLong(MemberBeer.TASTED_DATE);

            cvMergeBeer.put(MemberBeer.COMMENTS, comments);
            cvMergeBeer.put(MemberBeer.RATING, rating);
            cvMergeBeer.put(MemberBeer.TASTED_DATE, tasted);

            cvBeers.remove(MetaBeer.COMMENTS);
            cvBeers.remove(MetaBeer.RATING);
            cvBeers.remove(MemberBeer.TASTED_DATE);


            bulkInsertBeers.add(cvBeers);
            bulkInsertStoreBeer.add(cvMergeBeer);


        }

        int resBeers = mContentResolver.bulkInsert(Beers.CONTENT_URI, bulkInsertBeers.toArray(new ContentValues[]{}));

        Cursor q = this.mContentResolver.query(
                Beers.CONTENT_URI,
                Util.asAr(
                        Beers.BEER_ID,
                        Beers.BEERS_KEY
                ),
                null, null,
                Beers.BEERS_KEY
        );

        ContentQueryMap ccc = new ContentQueryMap(q, Beers.BEER_ID, false, null);
        Map<String, ContentValues> previousMap = ccc.getRows();


        for (int i = 0; i < bulkInsertStoreBeer.size(); i++) {
            ContentValues curBeerId = userBeerListIn.get(i);
            ContentValues curMergeBeer = bulkInsertStoreBeer.get(i);

            String currentToInsertBeerId = curBeerId.getAsString(Beers.BEER_ID);
            Long currentToInsertBeerDbId = previousMap.get(currentToInsertBeerId).getAsLong(BaseColumns._ID);
            curMergeBeer.put(MemberBeer.BEER_DB_ID, currentToInsertBeerDbId);
        }
        ccc.close();
        q.close();

        int resStoreBeer = this.mContentResolver.bulkInsert(mergeTable, bulkInsertStoreBeer.toArray(new ContentValues[]{}));

        String resString = String.format("resBeers: %d, ResMergeBeer: %d", resBeers, resStoreBeer);
        Ln.d(resString);

    }

    private void insertOrUpdateStatus(List<ContentValues> userBeerListIn, Date now, String identKey) {
        List<ContentValues> bulkInsertBeers = new ArrayList<ContentValues>(userBeerListIn.size());
        List<ContentValues> bulkInsertStoreBeer = new ArrayList<ContentValues>(userBeerListIn.size());

        long timeNow = now.getTime();

        for (int i = 0; i < userBeerListIn.size(); i++) {
            ContentValues cvBeers = new ContentValues(userBeerListIn.get(i));
            ContentValues cvMergeBeer = new ContentValues();

            String comments = cvBeers.getAsString(MetaBeer.COMMENTS);
            Integer rating = cvBeers.getAsInteger(MetaBeer.RATING);
            Long tasted = cvBeers.getAsLong(MemberBeer.TASTED_DATE);

            cvMergeBeer.put(MemberBeer.COMMENTS, comments);
            cvMergeBeer.put(MemberBeer.RATING, rating);

            //THIS IS VERY IMPORTANT LEAVE THIS IN HERE
            if (tasted != null) {
                cvMergeBeer.put(MemberBeer.TASTED_DATE, tasted);
            }

            cvBeers.remove(MetaBeer.COMMENTS);
            cvBeers.remove(MetaBeer.RATING);
            cvBeers.remove(MemberBeer.TASTED_DATE);

            bulkInsertBeers.add(cvBeers);
            bulkInsertStoreBeer.add(cvMergeBeer);

        }

        int beersUpdateRes = mContentResolver.bulkInsert(Beers.CONTENT_URI, bulkInsertBeers.toArray(new ContentValues[]{}));

        Cursor q = this.mContentResolver.query(
                Beers.CONTENT_URI,
                Util.asAr(
                        Beers.BEER_ID,
                        Beers.BEERS_KEY
                ),
                null,
                null,
                Beers.BEER_ID
        );

        ContentQueryMap ccc = new ContentQueryMap(q, Beers.BEER_ID, false, null);
        Map<String, ContentValues> previousMap = ccc.getRows();

//		List<ContentValues> bulkInsertStoreBeer = new ArrayList<ContentValues>();

        for (int i = 0; i < userBeerListIn.size(); i++) {

            ContentValues cvOriginalBeers = userBeerListIn.get(i);
            ContentValues cvStoresBeers = bulkInsertStoreBeer.get(i);

            String currentToInsertBeerId = cvOriginalBeers.getAsString(Beers.BEER_ID);
            Long currentToInsertBeerDbId = previousMap.get(currentToInsertBeerId).getAsLong(BaseColumns._ID);

//			String comments = cvBeers.getAsString(MetaBeer.COMMENTS);
//			String rating = cvBeers.getAsString(MetaBeer.RATING);
//			Long tasted = cvBeers.getAsLong(MemberBeer.TASTED_DATE);
//			cvStoresBeers.put(MemberBeer.COMMENTS, comments);
//			cvStoresBeers.put(MemberBeer.RATING, rating);
//			cvStoresBeers.put(MemberBeer.TASTED_DATE, tasted);
//			cvBeers.remove(MetaBeer.COMMENTS);
//			cvBeers.remove(MetaBeer.RATING);
//			cvBeers.remove(MemberBeer.TASTED_DATE);

            cvStoresBeers.put(MemberBeer.UPDATED_DATE, timeNow);
            cvStoresBeers.put(MemberBeer.ACTIVE, true);
            cvStoresBeers.put(MemberBeer.MEMBER_DB_ID, identKey);
            cvStoresBeers.put(MemberBeer.BEER_DB_ID, currentToInsertBeerDbId);

            previousMap.remove(cvOriginalBeers.getAsString(Beers.BEER_ID));

//			bulkInsertStoreBeer.add(cvStoresBeers);
        }

        List<Long> leftOverBeerIds = new ArrayList<Long>();
        for (Entry<String, ContentValues> cur : previousMap.entrySet()) {
            leftOverBeerIds.add(cur.getValue().getAsLong(BaseColumns._ID));
        }
        ccc.close();
        q.close();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < leftOverBeerIds.size(); i++) {
            sb.append(leftOverBeerIds.get(i) + " , ");
        }
        if (sb.length() > 0 && sb.indexOf(",") > 0) {
            sb.setCharAt(sb.lastIndexOf(","), ' ');
        }

        ContentValues newCv = new ContentValues();
        newCv.put(MemberBeer.ACTIVE, false);
        newCv.put(MemberBeer.UPDATED_DATE, timeNow);
        //TODO is this working?
        int leftOverResult = mContentResolver.update(MemberBeer.CONTENT_URI, newCv, MemberBeer.MEMBER_DB_ID + "=? AND " + MemberBeer.BEER_DB_ID + " IN (" + sb.toString() + ")", Util.asAr(identKey));

        int beersStoresUpdateRes = mContentResolver.bulkInsert(MemberBeer.CONTENT_URI, bulkInsertStoreBeer.toArray(new ContentValues[]{}));

        ContentValues newCv2 = new ContentValues();
        newCv2.put(MemberBeer.CREATION_DATE, timeNow);
        int createdBeers = this.mContentResolver.update(MemberBeer.CONTENT_URI, newCv2, MemberBeer.MEMBER_DB_ID + "=? AND " + MemberBeer.CREATION_DATE + " ISNULL", Util.asAr(identKey));

        String resString = String.format("resBeers: %d, ResStoreBeer: %d, leftOverResult: %d, createdBeers: %d", beersUpdateRes, beersStoresUpdateRes, leftOverResult, createdBeers);
        Ln.d(resString);
    }

//    @Inject
//    ConvertMaster mConvertMaster;

    @Override
    public void masterRefresh() throws IOException, HttpException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ParseException {

        Ln.i("MemberBeerListModel: masterRefresh began");

        if (UfoClubApp.DEBUG_DISABLE_REFRESH_DATABASE) {
            Ln.i("REFRESHING DATABASE IS TURNED OFF.");
            return;
        }


        List<ContentValues> tastedList = null;
        List<ContentValues> untastedList = null;
        Map<String, Object> datesRet = null;
        Map<String, List<String>> stylesRet = null;

//            try {
        tastedList = ufoClubClient.getTastedPage();
//            } catch (Exception e) {
//                Ln.e("Error getting tasted list.", e);
//                throw new
//            }
//            try {
        untastedList = ufoClubClient.getUntastedPage();
//            } catch (Exception e) {
//                Ln.e("Error getting un-tasted list.", e);
//                return;
//            }
//            //Dates->Beer Names
//            try {
//                datesRet = regexQueries.getDatePage();
//            } catch (Exception e) {
        datesRet = new HashMap<String, Object>();
//                Ln.e("Error getting date page.", e);
//                return;
//            }
//            try {
//                stylesRet = regexQueries.getStylesPage();
//            } catch (Exception e) {
        stylesRet = new HashMap<String, List<String>>();
//                Ln.e("Error getting styles page.", e);
//                return;
//            }

        //Styles Names->Beer Names
//			Map<String, Object> newMap = new HashMap<String,Object>();
//			for (Entry<String,List<String>> entry : stylesRet.entrySet()) {
//				for (String beerName : entry.getValue()) {
//					newMap.put(beerName, entry.getKey());
//				}
//			}

        if (shouldCancel()) {
            return;
        }


//            try {
        UfoClubClientImpl.mergeDatePage(datesRet, tastedList, "addDatePropertiesCP");
//            } catch (Exception e) {
//                Ln.e("Error merging date page.", e);
//            }

//            try {
        UfoClubClientImpl.mergeStylePage(stylesRet, tastedList, "addStyleProperties");
//            } catch (Exception e) {
//                Ln.e("Error merging styles page.", e);
//            }


        if (shouldCancel()) {
            return;
        }

//				app.getBeerWebLink().mergeStylePage(tastedList);
//			for (ContentValues cv: untastedList) {
//				cv.put(MemberBeer.TASTED_DATE, false);
//			}

//			} catch (Exception e) {
//				e.printStackTrace();
////				this.fireFailureSticky(e.getMessage());
//				//TODO fix this?
//				app.setLastBeerLookup(null);
//				return;
//			}    	

//			List<ContentValues> combinedList = new ArrayList<ContentValues>(tastedList.size()+untastedList.size());
//			combinedList.addAll(tastedList);
//			combinedList.addAll(untastedList);

        Integer memberId = credentials.get().getCredentials().getMemberId();
//            Long memberDbId = MemberModule.getMemberDbId(this.mContentResolver, memberId);

        Long memberDbId = memberDbIdProvider.get();
        if (memberDbId == null) {
            Ln.w("MemberDbId should not be null.");
            return;
        }

        Date now = Calendar.getInstance().getTime();
        if (isEmpty.get()) {
            //if it's empty, it's likely the first time it's being loaded
            //close the old db which should be the -1 identified db
            //set it null so when the getter is called again, it will return
            //a newly constructed db with the correct member_id
//				app.getBeerDB().close();
//				app.setBeerDB(null);
            //TODO this is wrong LOL!

            Ln.i("Database is empty, do clean insert.");

            insertAllMember(tastedList, now, memberDbId.toString());
            insertAllMember(untastedList, now, memberDbId.toString());
            credentials.get().getCredentials().setOriginDate(now);

            ContentValues cvOriginDate = new ContentValues();
            cvOriginDate.put(Members.UFO_ORIGIN_DATE, now.getTime());

            mContentResolver.update(Members.withId(memberDbId.toString()), cvOriginDate, null, null);

            credentials.get().bindToPreferences();
        } else {
            Ln.i("Database exists and is being updated.");
            insertOrUpdateStatus(tastedList, now, memberDbId.toString());
            insertOrUpdateStatus(untastedList, now, memberDbId.toString());
        }

        Ln.i("Updating refreshed date");
        ContentValues cvBeerRefreshedDate = new ContentValues();
        cvBeerRefreshedDate.put(Members.BEERS_REFRESHED_DATE, now.getTime());
        mContentResolver.update(Members.withId(memberDbId.toString()), cvBeerRefreshedDate, null, null);

        Ln.i("MemberBeerListModel: masterRefresh finished");


    }

    public synchronized Boolean shouldCancel() {
        return shouldCancel;
    }

    public synchronized void setShouldCancel(Boolean choice) {
        shouldCancel = choice;
    }

    @Override
    public synchronized Boolean needsRefresh() {
        Boolean ret = false;

        Builder b = Members.CONTENT_URI.buildUpon();
//		b.appendQueryParameter(FlyingSaucerConv.ACTIVE_BY, "true");

        Cursor c = this.mContentResolver.query(b.build(), Util.asAr(Members.BEERS_REFRESHED_DATE, Members.MEMBERS_KEY, Members.UFO_MEMBER_ID),
                Members.UFO_MEMBER_ID + "=?",
                Util.asAr(memberIdProvider.get()),
                null);

        if (!c.moveToFirst()) {
            ret = true;
            c.close();
            return ret;
        }
        Long ds = c.getLong(c.getColumnIndex(Members.BEERS_REFRESHED_DATE));
        c.close();
        if (ds == null) {
            ret = true;
            return ret;
        }
        Long d = ds;
        Date date = new Date();
        date.setTime(d);
        ret = Util.isTimeElapsed(date, 15);
        return ret;
    }

    //TODO remove the stuff below???

    @Override
    public synchronized BeerUri getBeerUri() {
        if (this.beerUri == null) {
            return beerUriProvider.get();
        } else {
            return this.beerUri;
        }
    }

    @Override
    public synchronized void setBeerUri(BeerUri beerUri) {
        this.beerUri = beerUri;
    }


}
